#include<linux/module.h>
#include<linux/interrupt.h>
#include<linux/proc_fs.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Tan xujia");
MODULE_VERSION("V1");

static struct work_struct work;
unsigned long flags;

/*下半部/底半部*/
void 
workqueue_fn(struct work_struct *work)
{
    printk("workqueue_fn\n");
}

/*上半部/顶半部*/
static irqreturn_t 
irq_handler(int irq,void *dev)
{
    static int n = 0;
    printk("irqNumber = %d, irqCount = %d\n", irq, ++n);
    /*将工作交给系统的工作队列进行调度*/
    schedule_work(&work);
    return IRQ_HANDLED;
}

/*
需要注意的是，disable_irq执行了几次，
enable_irq也需要执行几次。
*/
ssize_t
hp_write(struct file * filp, const char __user *buff, size_t count, loff_t * f_pos)
{
    char a;
    get_user(a,buff);
    if(a == '0') {
        printk("disable irq\n");
        /*禁止中断以后，触发中断，则中断的回调函数不会执行*/
        disable_irq(gpio_to_irq(12));
    } else {
        printk("enable irq\n");
        /*使能指定的中断*/
        enable_irq(gpio_to_irq(12));
    }
    return count;
}

struct file_operations hp_ops = {
    .write = hp_write,
};

static int
__init hello_init(void)
{
    int err;
    /*创建一个接口，可以控制中断的打开和关闭*/
    proc_create("proc_interrupt", 0777, NULL, &hp_ops);
    /*初始化一个工作*/
    INIT_WORK(&work, workqueue_fn);
    /*申请一个中断号*/
    err = request_irq(gpio_to_irq(12), irq_handler, IRQ_TYPE_EDGE_BOTH, "interruptTest", NULL);
    if(err<0) {
        printk("irq_request failed\n");
        remove_proc_entry("proc_interrupt", NULL);
        return err;
    }
    return 0;
}

static void
__exit hello_exit(void)
{
    printk("hello_exit\n");
    /*释放申请的中断号*/
    free_irq(gpio_to_irq(12), NULL);
    remove_proc_entry("proc_interrupt", NULL);
}

module_init(hello_init);
module_exit(hello_exit);